/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package adversarial;

import agents.AgentState;
import env.Action;
import env.Enviroment;
import env.Tile;
import java.util.Arrays;
import java.util.Vector;

/**
 *
 * @author user
 */
public class State implements Comparable<State> {
    protected AgentState firstAgent; //state of player
    protected AgentState secondAgent; //state of enemy
    protected Vector<Tile> flags; //available flags
    private Action lastAction; //what move by the second agent resulted in this state?
    public volatile int remainingMoves;
    
    public State(Vector<Tile> flags, AgentState firstAgent, AgentState secondAgent, Action lastAction, int movesRemaining) {
        this.flags = flags;
        this.firstAgent = firstAgent;
        this.secondAgent = secondAgent;
        this.lastAction=lastAction;
        this.remainingMoves=movesRemaining;
    }

    /**
     * AI
     * this is for heuristic needs!!
     * minimax is optimal regardless of ordering!
     *
     * the ordering is from WORST to BEST for the successor's first player,
     * thus it's ordered from BEST to WORST for this node's first player!
     * @param o
     * @return
     */
    @Override
    public int compareTo(State o) {
        //with score, more is better
        final int util = o.secondAgent.getScore() - secondAgent.getScore();
        
        //System.out.println("compareTo: "+this+"  compared to "+o+": \t util="+util+"   dist="+dist);
        if (util != 0) {
            return util;
        }

        //with dist, less is better
        final int dist = dist_to_nearest_flag(secondAgent) - dist_to_nearest_flag(o.secondAgent);
        //AI
        //this part always leads to better pruning, but often isn't worth it.
        //it IS used, because sometimes it can help the agent avoid stupid remainingMoves,
        //namely the "wasting remainingMoves doing "shoot" on a loop near close flags" phenomena
        if(dist!=0)
            return dist;


        //more bullets is better
        return o.secondAgent.getBullets() - secondAgent.getBullets();
    }

    public int score() {
        if(Enviroment.ZERO_SUM_GAME)
            return firstAgent.getScore() - secondAgent.getScore();
        return firstAgent.getScore();
    }

    private static final HeuristicBFS bfs=new HeuristicBFS();
    private int dist_to_nearest_flag(AgentState state) {
        return bfs.dist_nearest_flag(state.getTile(), flags);
//        int dist = 1000; //dist to nearst flag
//        Tile current = state.getTile();
//        for (Tile flag : flags) {
//            int temp = flag.manhattan(current);
//            if (temp < dist) {
//                dist = temp;
//            }
//        }
//        return dist;
    }


    /**
     * @return the successors. 
     * could be in any order, but it's actually sorted.
     */
    public State[] expand() {
//        if(useHash)
//            return expand_hash();
        if (!firstAgent.isAlive()) {
            State[] ans = new State[1];
            ans[0] = new State(flags, secondAgent, firstAgent, null, remainingMoves-1);
            return ans;
        }
         State[] ans = new State[5];
        
        int i = 0;
        for (Action move : Action.values()) {
            State next = makeMove(move);
            ans[i++]=next;
        }

        Arrays.sort(ans); //AI <---------- this is just an heuristic imporvement! (or is it?)
        return ans;
    }

    Action getLastAction() {
        return lastAction;
    }

    boolean isTerminal() {
        return flags.isEmpty() || remainingMoves==0;
    }

    private Vector<Tile> nextFlags(Tile nextTile) {
        if (!flags.contains(nextTile)) {
            return flags;
        }
        final Vector<Tile> nextFlags = new Vector<Tile>(flags.size() - 1);
        for (Tile f : flags) {
            if (f != nextTile) {
                nextFlags.add(f);
            }
        }
        return nextFlags;
    }

    private State makeMove(Action move) {
        //why does it matter that i actually clone this?
        //i'm pretty sure that's it's because of the possibility of killing
        final AgentState nextFirst = secondAgent.clone(null); 
        final AgentState nextSecond = firstAgent.clone(move);

        final Tile nextTile = nextSecond.getTile();
        if (flags.contains(nextTile)) {
            nextSecond.incScore(Enviroment.FLAG_REWARD);
        }

        final Vector<Tile> nextFlags = nextFlags(nextTile);

        if (move == Action.shoot &&
            firstAgent.getBullets()>0 &&
            Enviroment.isInLineOfSight(nextFirst.getRow(), nextFirst.getCol(), nextSecond.getRow(), nextSecond.getCol())) {
                nextFirst.setAlive(false);
        }

        return new State(nextFlags, nextFirst, nextSecond, move, remainingMoves-1);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final State other = (State) obj;
        if (this.firstAgent != other.firstAgent && (this.firstAgent == null || !this.firstAgent.equals(other.firstAgent))) {
            return false;
        }
        if (this.secondAgent != other.secondAgent && (this.secondAgent == null || !this.secondAgent.equals(other.secondAgent))) {
            return false;
        }
        if (this.flags != other.flags && (this.flags == null || !this.flags.equals(other.flags))) {
            return false;
        }
        if (this.remainingMoves != other.remainingMoves) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 59 * hash + (this.firstAgent != null ? this.firstAgent.hashCode() : 0);
        hash = 59 * hash + (this.secondAgent != null ? this.secondAgent.hashCode() : 0);
        hash = 59 * hash + (this.flags != null ? this.flags.hashCode() : 0);
        hash = 59 * hash + this.remainingMoves;
        return hash;
    }
    

    @Override
    public String toString() {
        return "State: "+getLastAction()+"  =>  first=<" + firstAgent + ">    second=<" + secondAgent + ">    f=" + flags.size();
    }
}
